#include <kutils.h>
#include <string.h>
#include "vfs.h"
#include "memory.h"
#include "display.h"
#include "keyboard.h"

fs_node_t *fs_find_child(fs_node_t *node, s8int *name);

fs_node_t *fs_root = 0; // The root of the filesystem.
u32int next_inode = 1;
struct dirent ent;

u32int get_valid_inode(void)
{
	return next_inode++;
}

//backtraces from the given node to the root finding the full path name of the node
s8int* fs_fill_path(fs_node_t* node, s8int* path)
{
	s8int temp_table[32][128];
	u32int table_index = 0;
	fs_node_t* temp_node = node;

	memset(path, 0, strlen(path));

	while(temp_node != fs_root)
	{
		strcpy(temp_table[table_index], (s8int*)temp_node->name);
		table_index++;
		temp_node = temp_node->parent;
		if(table_index > 31)
			break;
	}
	while(table_index)
	{
		table_index--;
		strcat(path, "/");
		strcat(path, temp_table[table_index]);
	}
	return path;
}

s32int scan_fs(fs_node_t* node)
{
	if(!node)
		return 0;

	if (node->scan != 0)
		return node->scan(node);
	else
		return 0;
}

struct dirent *readdir_fs(fs_node_t *node, u32int index)
{
	//if it's not a directory then it can't be "read"
	if(node->flags != FS_DIRECTORY)
	{
		return 0;
	}

	// If node has no children, perform a scan in it to build a list of children.
	if(!node->first_child)
		scan_fs(node);

	u32int n = 0;
	fs_node_t *tmp_node = node->first_child;
	while(tmp_node)
	{
		if(n == index)
		{
			strcpy((s8int*)&ent.name,(const s8int*)tmp_node->name);
			ent.ino = tmp_node->inode;
			return &ent;
		}
		
		tmp_node = tmp_node->next_sibling;
		n++;
	}
	return 0;
}

//searches the given nodes' children for one that matches the given name. Returns null if it doesn't exist.
fs_node_t *fs_find_child(fs_node_t *node, s8int *name)
{
	if(!node)
		return 0;

	// If node has no children, perform a scan in it to build a list of children.
	if(!node->first_child)
		scan_fs(node);

	fs_node_t *tmp_node = node->first_child;
	while(tmp_node)
	{
		if(strcmp((const s8int*)tmp_node->name,(const s8int*)name) == 0)
			return tmp_node;
		tmp_node = tmp_node->next_sibling;
	}
	return 0;
}

fs_node_t *finddir_fs(fs_node_t *node, s8int *name)
{
	if(!name)
		return 0;

	fs_node_t *tmp_node;
	s32int si = 0;
	s32int di = 0;
	s8int tmp_name[128];

	if(name[0] == '/')//if this is an absolute path...
	{
		tmp_node = fs_root;
		si = 1;
	}
	else
	{
		if(!node)
			return 0;
		tmp_node = node;
	}

	while(name[si])//while we haven't fallen off of the end of the name
	{
		if(name[si] == '/')
		{
			tmp_name[di] = 0;

			//handle some special path names
			if(strcmp(tmp_name,".") == 0)
			{
				//the directory should stay the same
			}
			else if(strcmp(tmp_name,"..") == 0)
			{
				tmp_node = tmp_node->parent;
			}
			else
			{
				tmp_node = fs_find_child(tmp_node, tmp_name);
				if(!tmp_node)
					return 0;
			}
			memset(tmp_name,0,128);
			di = 0;
		}
		else
		{
			tmp_name[di] = name[si];
			di++;
		}
		si++;
	}

	//if the destination index is not zero then that means that we should finish the search now.
	if(di)
	{
		tmp_name[di] = 0;

		//handle some special path names
		if(strcmp(tmp_name,".") == 0)
		{
			//the directory should stay the same
		}
		else if(strcmp(tmp_name,"..") == 0)
		{
			tmp_node = tmp_node->parent;
		}
		else
		{
			tmp_node = fs_find_child(tmp_node, tmp_name);
			if(!tmp_node)
				return 0;
		}
	}

	return tmp_node;
}

s32int seek_fs(fs_node_t* node, u32int offset)
{
	if(!node)
		return 0;

	node->position = offset;
	return 1;
}

fs_node_t* open_fs(s8int* path)
{
	fs_node_t *tmp_node = finddir_fs(fs_root, path);
	if(tmp_node)
	{//file exists
		// Has the node got an open callback?
		if (tmp_node->open != 0)
		{
			if(tmp_node->flags & FS_FILE)
			{
				if(tmp_node->open(tmp_node))
				{//open succeded. Return node.
					return tmp_node;
				}
				else
				{//open failed
					return 0;
				}
			}
			else
			{// not a file
				return 0;
			}
		}
		else
		{//no open callback
			return 0;
		}
	}
	else
	{//file doesn't exist
		return 0;
	}
}

s32int close_fs(fs_node_t* node)
{
	if(!node)
		return 0;

  // Has the node got a close callback?
  if (node->close != 0)
    return node->close(node);
  else
    return 0;
}

s32int read_fs(fs_node_t* node, u32int size, u8int *buffer)
{
	if(!node)
		return 0;
  	// Has the node got a read callback?
  	if (node->read != 0)
	{
		s32int success = node->read(node, node->position, size, buffer);
		node->position += size;
		return success;
	}
	else
	{
		return 0;
	}
} 

s32int write_fs(fs_node_t* node, u32int size, u8int *buffer)
{
	if(!node)
		return 0;
	// Has the node got a write callback?
	if (node->write != 0)
	{
		s32int success = node->write(node, node->position, size, buffer);
		node->position += size;
		return success;
	}
	else
		return 0;
}

void init_vfs(void)
{
#ifdef FLUIDIUM_DEBUG
	write_string("VFS: Init\n");
#endif
	//just setup the root file node and a /dev directory and return
	fs_root = (fs_node_t*)kmalloc(sizeof(fs_node_t));

	fs_root->name[0] = '/'; fs_root->name[1] = 0x00;
	fs_root->inode = get_valid_inode();
	fs_root->flags = FS_DIRECTORY;
	fs_root->read = 0;
   fs_root->write = 0;
   fs_root->open = 0;
   fs_root->close = 0;
   fs_root->ptr = 0;
   fs_root->dvr_num = 0;
	fs_root->dvr_ptr = 0;
	fs_root->parent = fs_root;//the root has no parent, so just have it wrap around to itself.

//*
	fs_node_t *fs_dev = (fs_node_t*)kmalloc(sizeof(fs_node_t));
	strcpy((s8int*)fs_dev->name, "devices");
	fs_dev->inode = get_valid_inode();
	fs_dev->flags = FS_DIRECTORY;
	fs_dev->read = 0;
   fs_dev->write = 0;
   fs_dev->open = 0;
   fs_dev->close = 0;
   fs_dev->ptr = 0;
	fs_dev->dvr_num = 0;
	fs_dev->dvr_ptr = 0;
	fs_dev->parent = fs_root;//it's parent is the root

	fs_root->first_child = fs_dev;
//*/
	return;
}

